Skip to content

refactor: add storage trait abstraction for ValidatorDB and unit tests#111

Open
flyq wants to merge 1 commit intomainfrom
liquan/refator2
Open

refactor: add storage trait abstraction for ValidatorDB and unit tests#111
flyq wants to merge 1 commit intomainfrom
liquan/refator2

Conversation

@flyq
Copy link
Copy Markdown
Member

@flyq flyq commented Mar 31, 2026

Summary

Introduce storage trait abstractions (ChainState, TaskQueue, BlockDataStore, ValidationResultStore) that decouple consumers from the concrete redb-backed ValidatorDB implementation, enabling alternative backends (e.g., in-memory for testing) without changing consumer code.

  • Add storage_traits.rs with four domain-specific traits covering chain state management, task queue lifecycle, block/witness data retrieval, and validation result storage
  • Implement all four traits on ValidatorDB via delegation to existing inherent methods (purely additive, no behavior change)
  • Refactor chain_sync.rs to use trait bounds (DB: ChainState + TaskQueue) instead of concrete &ValidatorDB / Arc<ValidatorDB> parameters
  • Add comprehensive ValidatorDB unit tests covering chain growth & promotion, rollback, genesis round-trip, anchor block, contract code cache, block hash lookup, earliest local block, and history pruning
  • Add tempfile dev-dependency for test database isolation

Details

Storage Traits (storage_traits.rs)

Trait Responsibility
ChainState Local/remote chain tips, growth, rollback, genesis, anchor, pruning
TaskQueue Validation task creation, claiming, and recovery
BlockDataStore Block/witness retrieval and contract bytecode cache
ValidationResultStore Validation result storage and lookup

chain_sync.rs Generalization

fetch_blocks_batch, remote_chain_tracker, and find_divergence_point now accept generic DB parameters bounded by the appropriate traits. Call sites in binaries continue to pass ValidatorDB unchanged since it implements all traits.

Unit Tests (8 tests)

  • chain_growth_and_promotion — anchor + remote growth + promote to canonical
  • rollback — grow chain then roll back to earlier block
  • genesis_round_trip — store and reload genesis config
  • anchor_block_round_trip — reset and retrieve anchor block
  • contract_code_cache — add and query contract bytecodes
  • get_block_hash_canonical_and_remote — hash lookup across both chains
  • earliest_local_block — empty chain and post-anchor queries
  • prune_history — prune old canonical entries

Test plan

  • cargo check --workspace passes
  • cargo test -p stateless-core passes (includes 8 new tests)
  • cargo clippy --workspace --all-targets --all-features clean

@flyq flyq requested a review from Troublor as a code owner March 31, 2026 09:49
@claude
Copy link
Copy Markdown

claude bot commented Mar 31, 2026

This PR currently has no labels. Based on the diff and description, the enhancement label appears to be missing.

The PR adds new storage trait abstractions (ChainState, TaskQueue, BlockDataStore, ValidationResultStore), refactors chain_sync.rs to use generic trait bounds, and adds 8 new unit tests — all of which constitute new functionality/improvements to the codebase rather than a bug fix.

Of the available labels, enhancement ("New feature or request") is the most appropriate fit.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

fn temp_db() -> ValidatorDB {
let dir = tempfile::tempdir().unwrap();
let db = ValidatorDB::new(dir.path().join("test.redb")).unwrap();
std::mem::forget(dir); // OS cleans up when process exits
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::mem::forget is the wrong tool here — it leaks the TempDir and never cleans it up (even though it works by accident, since the directory outlives the test). The idiomatic fix is to return the TempDir from the helper so its drop runs when the test ends:

Suggested change
std::mem::forget(dir); // OS cleans up when process exits
let dir = tempfile::tempdir().unwrap();
let db = ValidatorDB::new(dir.path().join("test.redb")).unwrap();
(db, dir)

Change the return type to (ValidatorDB, tempfile::TempDir) and update each call site to let (db, _dir) = temp_db();.

Comment on lines +380 to +381
let headers: Vec<_> = tasks.iter().map(|(block, _, _)| block.header.clone()).collect();
db.grow_remote_chain(&headers)?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The &[Header] trait signature forces an allocation + full clone of every header on the hot path. The original code passed an iterator with no allocation:

// before
db.grow_remote_chain(tasks.iter().map(|(block, _, _)| &block.header))?;

Consider changing the trait method to accept impl IntoIterator<Item = &Header> — though that isn't directly expressible in a trait method today without a generic parameter. A practical middle ground: keep &[Header] in the trait but change the inherent ValidatorDB::grow_remote_chain to also accept &[Header] so the trait impl is a zero-cost pass-through, and fix the call site to avoid the clone by collecting &block.header into a Vec<&Header>:

Suggested change
let headers: Vec<_> = tasks.iter().map(|(block, _, _)| block.header.clone()).collect();
db.grow_remote_chain(&headers)?;
let headers: Vec<&Header> = tasks.iter().map(|(block, _, _)| &block.header).collect();
db.grow_remote_chain(&headers)?;

This still needs a Vec but at least avoids cloning each Header.

Comment on lines +1543 to +1546
db.rollback_chain(52).unwrap();
assert_eq!(db.get_local_tip().unwrap().unwrap().0, 52);
// Remote chain should also be rolled back
assert!(db.get_remote_tip().unwrap().is_none());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion doesn't test what the comment claims. All 5 blocks were promoted to canonical before rollback_chain is called, so the remote chain is already empty at that point — the assertion is vacuously true regardless of what rollback_chain does to the remote chain.

To actually test that rollback clears remote-chain state, leave some blocks unpromotod and verify they're gone after the rollback. For example, only promote 3 of the 5 blocks and then roll back to a point that includes the unpromoted range.

@claude
Copy link
Copy Markdown

claude bot commented Mar 31, 2026

Good direction — the trait abstractions are clean and the delegation impls are correct. Three issues to address:

  1. temp_db() leaks the temp directory (see inline) — use (ValidatorDB, TempDir) return type so cleanup is automatic.
  2. Unnecessary Header clone on the hot path (see inline) — the &[Header] trait signature forced a Vec<Header> + clone where an iterator sufficed before. At minimum collect Vec<&Header> to avoid the clone.
  3. Rollback test assertion is vacuous (see inline) — the remote chain is already empty before rollback_chain is called, so the "remote chain rolled back" assertion proves nothing. Needs a test with unpromoted blocks remaining in the remote chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant